// Record the last element to be right-clicked
var lastContextMenuTarget;
$("body").bind("contextmenu", function (e) {
    lastContextMenuTarget = e.target;
}).click(function () {
    lastContextMenuTarget = undefined;
});

if (chrome.runtime.onMessage) chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) {
    switch (request.action) {
        case "download":
            if (!lastContextMenuTarget) {
                console.log("not target to transform");
            } else {
                console.log("target is: ", lastContextMenuTarget);
            }
            return true;
        case "applyJQuery":
            $.when(applyJQuery(document, request.selector, request.target)).then(function (result) {
                console.log("found %d downloadables, seding response...", result.length);
                sendResponse({action: "processDownloadables", dataSet: result});
            });
            return true;
        case "applyXPath":
            $.when(applyXPath(document, request.selector, request.target)).then(function (result) {
                console.log("found %d items, seding response...", result.length);
                sendResponse({action: "processDownloadables", dataSet: result});
            });
            return true;
        case "crawl":
            $(document).crawl({targets: request.selector, depth: 3}).progress(function (info) {
                chrome.runtime.sendMessage({action: "updateInfo", text: info});
            }).then(function (linksSet) {
                console.log("found %d items, seding response...", linksSet.size);
                var dataSet = [];
                linksSet.forEach(function (link) {
                    dataSet.push({
                        id: 0,
                        url: link
                    });
                });
                sendResponse({action: "processDownloadables", dataSet: dataSet});
            });
            return true;
        default:
            console.log("cannot handle action: %s", request.action);
            return {action: "error", text: "cannot handle action: " + request.action};
    }
});

/**
 * Originally this function was executed as a callback of the local storage get. Should I remove the deferred?
 * Maybe not if I want to make it a spider...
 *
 * @param root
 * @param selector
 * @param context
 * @returns {*}
 */
function applyJQuery(root, selector, target) {

    //$.get("http://www.retro64games.net/C64files/A_2.html", function (data) {
    //    $.when(extractDataSet(document)).then(function (result) {
    //        // sendResponse({ action: "add", selector: result.selector, dataSet: result.dataSet  });
    //     });
    //});

    console.log("selector", selector);
    console.log("target", target);

    var deferred = $.Deferred();
    try {
        var downloadables = [];

        $(root).find(selector).each(function (index, element) {
            var $element = $(element);

            var url = $element.attr(target);
            downloadables.push({
                "id": element.id,
                "url": url
            });
        });

        deferred.resolve(downloadables);

    } catch (e) {
        deferred.reject(e);
    }
    return deferred.promise();
}

/**
 *
 * @param root
 * @param selector
 * @param target
 * @returns {*}
 */
function applyXPath(root, selector, target) {

    console.log("selector", selector);
    console.log("target", target);

    var deferred = $.Deferred();
    try {
        var downloadables = [];

        var result = $(root).xpath(selector);

        console.log(result);

        deferred.resolve(result);

    } catch (e) {
        deferred.reject(e);
    }
    return deferred.promise();
}

$.targets = function (element) {

    var $element = $(element);

    var url = $element.attr('href');
    return {
        "id": element.id,
        "url": url
    }
}

$.fn.crawl = function (options) {

    var $d = $.Deferred();

    var sources = options.sources || "a[href$='.html']";
    var targets = options.targets;
    var queue = new Array();
    var visited = new Set();
    visited.add(location.href);
    var links = new Set();

    var base = options.base || location.href;

    function resolve(element) {
        return new URI($(element).attr('href')).absoluteTo(base).toString();
    }

    (function recurse(depth) {
        return function (page) {
            if (page) try {
                var $html = $(page);
                $html.find(targets).each(function (index, element) {
                    var link = resolve(element);
                    if (!links.has(link)) {
                        console.info('adding %s to the links to be downloaded', link);
                        links.add(link);
                        $d.notify('added ' + link);
                    }
                });
                if (depth > 0) $html.find(sources).each(function (index, element) {
                    var url = resolve(element);
                    if (!visited.has(url)) {
                        console.info('added %s to the pages to be visited', url);
                        visited.add(url);
                        queue.push(url);
                    } else {
                        console.info('skipped %s because already visited', url);
                    }
                });
                (function advance() {
                    var url = queue.shift();
                    if (url) {
                        $d.notify('fetching ' + url);
                        console.info('fetching page: %s', url);
                        $.get(url).then(recurse(depth - 1), function (error) {
                            console.info('could not load %s', url);
                            advance();
                        });
                    } else {
                        console.info('done');
                        $d.resolve(links);
                    }
                })();
            } catch (e) {
                $d.reject(e);
            }
        }
    })(options.depth)(this[0]);

    return $d.promise();
};
